package com.tosmart.jorm.impl;

import java.util.*;

/**
 * The class implement a simple stack of such control
 * mechanisms, to prevent the foreign key association
 * (@ JormRefer and @ JormReferList) associated with
 * the same object, alternating, until a stack overflow.
 *
 * @author tosmart@gmail.com
 * @see com.tosmart.jorm.impl.JormImpl
 */
public class ReferStack {

    public static int DefaultReferDepth = 2;

    private static Map<Thread, ReferStack>
            pool = new HashMap<Thread, ReferStack>();

    private ReferStack() {
        referDepth = DefaultReferDepth;
    }

    /**
     * @return the instance for current thread.
     */
    public static ReferStack get() {

        ReferStack current = pool.get(
                Thread.currentThread()
        );

        if (current == null) {
            cleanDeadThreadStacks();
            current = new ReferStack();
            pool.put(Thread.currentThread(), current);
        }

        return current;
    }

    /**
     * Step in a Stack.
     *
     * @return true if success, false otherwise
     */
    public boolean forward() {

        if (depth < referDepth) {
            depth++;
            return true;
        }

        return false;
    }

    /**
     * Step out a Stack
     */
    public void back() {
        if (depth > 0) depth--;
    }

    /**
     * Getter for member referDepth
     * @return refer depth
     */
    public int getReferDepth() {
        return referDepth;
    }

    /**
     * Setter for member referDepth
     * @param referDepth the new depth value.
     */
    public void setReferDepth(int referDepth) {
        this.referDepth = referDepth;
    }

    /**
     * Clean dead thread stacks in the list of
     * stack instances.
     */
    private static void cleanDeadThreadStacks() {

        List<Thread> removeList = new ArrayList<Thread>();
        Set<Thread> threads = pool.keySet();

        for (Thread thread : threads) {
            if (!thread.isAlive())
                removeList.add(thread);
        }

        for (Thread thread : removeList) {
            pool.remove(thread);
        }
    }

    private int depth;
    private int referDepth;
}
